package config

import (
	"fmt"
	"os"

	"gitee.com/klein-x/biz/module/log"
	"github.com/knadh/koanf"
	"github.com/knadh/koanf/parsers/yaml"
	"github.com/knadh/koanf/providers/file"
	"github.com/knadh/koanf/providers/posflag"
	"github.com/spf13/pflag"
)

type LogConfig struct {
	LogPath  string `koanf:"path"`
	LogLevel string `koanf:"level"`
}

type ServerConfig struct {
	HttpHost string `koanf:"httphost"`
	HttpPort int    `koanf:"httpport"`
}

type RedisConfig struct {
	Addr string `koanf:"host"`
	Port int    `koanf:"port"`
	User string `koanf:"username"`
	Pwd  string `koanf:"password"`
}

type MysqlConfig struct {
	Addr string `koanf:"host"`
	Port int    `koanf:"port"`
	DB   string `koanf:"dbname"`
	User string `koanf:"username"`
	Pwd  string `koanf:"password"`
}

type Config struct {
	Log    LogConfig    `koanf:"log"`
	Server ServerConfig `koanf:"server"`
	Redis  RedisConfig  `koanf:"redis"`
	Mysql  MysqlConfig  `koanf:"mysql"`
}

var Conf Config

const yaml_split_tag string = "."
const cmd_split_tag string = "-"
const def_config_path string = "./config/klein.yaml"

const def_log_path = "./log/"
const def_log_level = "info"
const def_ser_httphost = "0.0.0.0"
const def_ser_httpport = 8999
const def_rds_addr = "127.0.0.1"
const def_rds_port = 6379
const def_msq_addr = "127.0.0.1"
const def_msq_port = 3306

func Init() bool {
	// 创建一个新的 koanf 实例
	flagSet := &pflag.FlagSet{}
	k := koanf.New(yaml_split_tag)
	// 1. 先解析命令行参数
	if err := parseCmdConfig(flagSet); err != nil {
		if err == pflag.ErrHelp {
			return false
		}
		log.PrePrintf("解析命令行参数失败,%v", err)
	}

	// 2. 获取config文件地址
	configFilePath, err := flagSet.GetString("config")
	if err != nil || configFilePath == "" {
		configFilePath = def_config_path
	}
	log.PrePrintf("config文件加载地址,%v", configFilePath)

	// 3. 设置配置文件到koanf
	if err := setYamlConfig(k, configFilePath); err != nil {
		log.PrePrintf("设置Config文件失败,%v", err)
	}

	// 4. 设置命令行到koanf
	if err := setCmdConfig(k, flagSet); err != nil {
		log.PrePrintf("设置Cmd命令失败,%v", err)
	}

	// 5. 映射koanf到结构体中
	if err := k.Unmarshal("", &Conf); err != nil {
		log.PrePrintf("映射Config结构体失败,%v", err)
	}

	// 6. 检查配置项并设置默认值
	if err := checkConfigAndSetDefault(); err != nil {
		log.PrePrintf("检测Config结构体失败,%v", err)
		return false
	}
	return true
}

func checkConfigAndSetDefault() error {
	// log
	if Conf.Log.LogPath == "" {
		Conf.Log.LogPath = def_log_path
		log.PrePrintf("检测到log_path为空,使用默认def_log_path,%v\n", def_log_path)
	}
	if Conf.Log.LogLevel == "" {
		Conf.Log.LogLevel = def_log_level
		log.PrePrintf("检测到log_level为空,使用默认def_log_level,%v\n", def_log_level)
	}
	// server
	if Conf.Server.HttpHost == "" {
		Conf.Server.HttpHost = def_ser_httphost
		log.PrePrintf("检测到ser_httphost为空,使用默认def_ser_httphost,%v\n", def_ser_httphost)
	}
	if Conf.Server.HttpPort == 0 {
		Conf.Server.HttpPort = def_ser_httpport
		log.PrePrintf("检测到ser_httpport为空,使用默认def_ser_httpport,%v\n", def_ser_httpport)
	}
	// redis
	if Conf.Redis.Addr == "" {
		Conf.Redis.Addr = def_rds_addr
		log.PrePrintf("检测到redis_addr为空,使用默认def_redis_addr,%v\n", def_rds_addr)
	}
	if Conf.Redis.Port == 0 {
		Conf.Redis.Port = def_rds_port
		log.PrePrintf("检测到redis_port为空,使用默认def_redis_port,%v\n", def_rds_port)
	}
	if Conf.Redis.User == "" {
		return fmt.Errorf("redis数据库的user不能为空")
	}
	if Conf.Redis.Pwd == "" {
		return fmt.Errorf("redis数据库的password不能为空")
	}
	// mysql
	if Conf.Mysql.Addr == "" {
		Conf.Mysql.Addr = def_msq_addr
		log.PrePrintf("检测到mysql_addr为空,使用默认def_mysql_addr,%v\n", def_msq_addr)
	}
	if Conf.Mysql.Port == 0 {
		Conf.Mysql.Port = def_msq_port
		log.PrePrintf("检测到mysql_port为空,使用默认def_mysql_port,%v\n", def_msq_addr)
	}
	if Conf.Mysql.DB == "" {
		return fmt.Errorf("mysql数据库的DB不能为空")
	}
	if Conf.Mysql.User == "" {
		return fmt.Errorf("mysql数据库的user不能为空")
	}
	if Conf.Mysql.Pwd == "" {
		return fmt.Errorf("mysql数据库的password不能为空")
	}
	return nil
}

func parseCmdConfig(flagSet *pflag.FlagSet) error {
	tmpFlagSet := &pflag.FlagSet{}
	// 使用 pflag 定义命令行参数
	//config path
	tmpFlagSet.String("config", "", "配置文件路径")
	//log
	tmpFlagSet.String("log-path", "", "日志路径")
	tmpFlagSet.String("log-level", "", "日志级别")
	//server
	tmpFlagSet.String("server-httphost", "", "http监听地址")
	tmpFlagSet.Int("server-httpport", 0, "http监听端口")
	//redis
	tmpFlagSet.String("redis-host", "", "ip地址")
	tmpFlagSet.Int("redis-port", 0, "端口号")
	tmpFlagSet.String("redis-user", "", "用户名")
	tmpFlagSet.String("redis-password", "", "密码")
	//mysql
	tmpFlagSet.String("mysql-host", "", "ip地址")
	tmpFlagSet.Int("mysql-port", 0, "端口号")
	tmpFlagSet.String("mysql-dbname", "", "数据库名")
	tmpFlagSet.String("mysql-user", "", "用户名")
	tmpFlagSet.String("mysql-password", "", "密码")

	set := func(flag *pflag.Flag, value string) error {
		flagSet.AddFlag(flag)
		return flagSet.Set(flag.Name, value)
	}

	return tmpFlagSet.ParseAll(os.Args[1:], set)
}

func setYamlConfig(k *koanf.Koanf, yamlFilePath string) error {
	if err := k.Load(file.Provider(yamlFilePath), yaml.Parser()); err != nil {
		return err
	}
	return nil
}

func setCmdConfig(k *koanf.Koanf, flagSet *pflag.FlagSet) error {
	if err := k.Load(posflag.Provider(flagSet, cmd_split_tag, k), nil); err != nil {
		return err
	}
	return nil
}
